package com.tl.satoken.netty;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.tl.satoken.domain.entity.*;
import com.tl.satoken.domain.vo.Heros;
import com.tl.satoken.domain.vo.GameCharacterVo;
import com.tl.satoken.enums.MessageType;
import com.tl.satoken.mapper.HeroMapper;
import com.tl.satoken.mapper.GameCharacterMapper;
import com.tl.satoken.service.RoomService;
import com.tl.satoken.service.GameRoomService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Slf4j
@Component
@ChannelHandler.Sharable
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<Object> {

    // 维护 广播的关键性代码
    private static final ConcurrentHashMap<String, ChannelHandlerContext> clients = new ConcurrentHashMap<>();  // clientId 与 ChannelHandlerContext 的映射
    private static final String CHAT_ROOM_KEY_PREFIX = "game:chat:room:";
    private static final long CHAT_HISTORY_TTL_DAYS = 90L;
    private static final ZoneId CHINA_ZONE = ZoneId.of("Asia/Shanghai");
    @Autowired
    private HeroMapper heroMapper;
    @Autowired
    private GameCharacterMapper gameCharacterMapper;
    @Autowired
    private  RoomService roomService;
    @Autowired
    private GameRoomService gameRoomService;

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 添加客户端连接
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        String clientId = ctx.channel().id().asShortText();
        clients.put(clientId, ctx);
        log.info("客户端[{}]连接成功，当前在线人数: {}", clientId, clients.size());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        String clientId = ctx.channel().id().asShortText();
        clients.remove(clientId);
        log.info("客户端[{}]断开连接，当前在线人数: {}", clientId, clients.size());

        // 从Redis中获取玩家信息并清理会话
        String sessionKey = "game:session:" + clientId;
        RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
        Player player = redisPlayer != null ? redisPlayer.toPlayer() : null;
        if (player != null && player.getRoomId() != null) {
            String roomId = player.getRoomId();
            Integer heroId = player.getHeroId();

            // 从房间中移除玩家，并检查是否掉落了金币
            boolean droppedCoins = roomService.leaveRoomWithCoinDrop(heroId);

            // 如果掉落了金币，广播coinBoom消息给房间内其他玩家
            if (droppedCoins) {
                broadcastCoinBoomMessage(roomId);
            }

            // 广播玩家离开消息给同房间其他玩家
            broadcastPlayerLeave(roomId, heroId);

            log.info("玩家 {} (heroId: {}) 离开房间 {}", heroId, heroId, roomId);
        }

        // 清理Redis中的会话信息
        redisTemplate.delete(sessionKey);

    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof TextWebSocketFrame) {
            String text = ((TextWebSocketFrame) msg).text();
            String clientId = ctx.channel().id().asShortText();
            log.info("收到客户端[{}]消息: {}", clientId, text);
            
            try {
                GameMessage gameMessage = objectMapper.readValue(text, GameMessage.class);
                handleGameMessage(ctx, clientId, gameMessage);
            } catch (Exception e) {
                log.error("解析游戏消息失败", e);
                sendError(ctx, "消息格式错误");
            }
        } else if (msg instanceof BinaryWebSocketFrame) {
            log.info("收到二进制消息");
        } else if (msg instanceof CloseWebSocketFrame) {
            log.info("客户端请求关闭连接");
            ctx.channel().close();
        } else if (msg instanceof PingWebSocketFrame) {
            log.info("收到Ping消息");
            ctx.channel().writeAndFlush(new PongWebSocketFrame());
        }
    }
    
    private void handleGameMessage(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        if (roomService == null) {
            return;
        }
        try {
            switch (message.getType()) {
                case enter:
                    handleJoinGame(ctx, clientId, message);
                    break;
                case getHeroes:
                    handleGetHeros(ctx, clientId);
                    break;
                case motion:
                    handleMotion(clientId, message);
                    break;
                case emotion:
                    handleEmotion(clientId, message);
                    break;
                case coinTailGrab:
                    handleCoinTailGrab(ctx, clientId, message);
                    break;
                case coinGroundGrab:
                    handleCoinGroundGrab(ctx, clientId, message);
                    break;
                case dropGround:
                    handleDropGround(ctx, clientId, message);
                    break;
                case gameMatch:
                    handleGameMatch(ctx, clientId, message);
                    break;
                case cancelMatch:
                    handleCancelMatch(ctx, clientId, message);
                    break;
                case gameEnd:
                    handleGameEnd(ctx, clientId, message);
                    break;
                case chat:
                    handleChat(ctx, clientId, message);
                    break;
                default:
                    log.warn("未知消息类型: {}", message.getType());
            }
        } catch (Exception e) {
            log.error("处理游戏消息失败", e);
            sendError(ctx, "处理消息失败: " + e.getMessage());
        }
    }

    private void handleEmotion( String clientId, GameMessage message) {
        try {
            // 从Redis中获取玩家会话信息
            String sessionKey = "game:session:" + clientId;
            RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
            Player sessionPlayer = redisPlayer != null ? redisPlayer.toPlayer() : null;
            
            if (sessionPlayer == null) {
                log.warn("发送表情包的玩家会话不存在: {}", clientId);
                return;
            }
            
            String roomId = sessionPlayer.getRoomId();
            if (roomId == null) {
                log.warn("玩家 {} 不在任何房间中", sessionPlayer.getHeroId());
                return;
            }
            
            // 获取表情包图片
            String image = message.getImage();
            if (image == null || image.trim().isEmpty()) {
                log.warn("玩家 {} 发送的表情包图片为空", sessionPlayer.getHeroId());
                return;
            }
            
            log.info("玩家 {} 发送表情包，房间: {}, 图片: {}", sessionPlayer.getHeroId(), roomId, image);
            
            // 广播表情包消息给房间内其他玩家，排除发送者自己
            broadcastEmotionMessage(roomId, sessionPlayer.getId(), sessionPlayer.getHeroId(), image);
            
        } catch (Exception e) {
            log.error("处理表情包消息失败", e);
        }
    }

    private void handleChat(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            String scope = message.getScope();
            if (scope == null || scope.trim().isEmpty()) {
                sendError(ctx, "聊天范围不能为空");
                return;
            }

            String content = message.getContent();
            if (content == null || content.trim().isEmpty()) {
                sendError(ctx, "聊天内容不能为空");
                return;
            }
            content = content.trim();

            String sessionKey = "game:session:" + clientId;
            RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
            Player sessionPlayer = redisPlayer != null ? redisPlayer.toPlayer() : null;
            if (sessionPlayer == null) {
                sendError(ctx, "玩家会话不存在");
                return;
            }

            Integer heroId = sessionPlayer.getHeroId();
            if (heroId == null) {
                sendError(ctx, "玩家英雄信息缺失");
                return;
            }

            long sendTime = Instant.now().atZone(CHINA_ZONE).toEpochSecond();
            if ("room".equals(scope)) {
                String roomId = sessionPlayer.getRoomId();
                if (roomId == null || roomId.trim().isEmpty()) {
                    sendError(ctx, "玩家不在任何房间中");
                    return;
                }

                ChatMessage chatMessage = ChatMessage.builder()
                        .type(MessageType.chat.name())
                        .scope("room")
                        .fromHeroId(heroId.longValue())
                        .toHeroId(message.getTo())
                        .roomId(roomId)
                        .content(content)
                        .sendTime(sendTime)
                        .build();

                storeRoomChatMessage(roomId, chatMessage);
                broadcastRoomChat(roomId, heroId, content, sendTime);
                log.info("玩家 {} 在房间 {} 发送聊天消息: {}", heroId, roomId, content);
            } else if ("friend".equals(scope) || "buddy".equals(scope)) {
                sendError(ctx, "暂未开通该聊天范围: " + scope);
            } else {
                sendError(ctx, "未知聊天范围: " + scope);
            }
        } catch (Exception e) {
            log.error("处理聊天消息失败", e);
            sendError(ctx, "处理聊天消息失败: " + e.getMessage());
        }
    }

    private void storeRoomChatMessage(String roomId, ChatMessage chatMessage) {
        try {
            String redisKey = CHAT_ROOM_KEY_PREFIX + roomId;
            redisTemplate.opsForList().rightPush(redisKey, chatMessage);
            redisTemplate.expire(redisKey, CHAT_HISTORY_TTL_DAYS, TimeUnit.DAYS);
        } catch (Exception e) {
            log.error("房间 {} 保存聊天记录失败", roomId, e);
        }
    }

    private void broadcastRoomChat(String roomId, Integer heroId, String content, long sendTime) {
        try {
            Map<String, Object> payload = new HashMap<>();
            payload.put("type", MessageType.chat.name());
            payload.put("from", heroId);
            payload.put("content", content);
            payload.put("sendTime", sendTime);
            String chatJson = objectMapper.writeValueAsString(payload);

            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            for (Player player : roomPlayers) {
                if (heroId.equals(player.getHeroId())) {
                    continue;
                }

                String targetClientId = player.getSessionId();
                if (targetClientId != null) {
                    ChannelHandlerContext targetCtx = clients.get(targetClientId);
                    if (targetCtx != null && targetCtx.channel().isActive()) {
                        targetCtx.channel().writeAndFlush(new TextWebSocketFrame(chatJson));
                    }
                }
            }
        } catch (Exception e) {
            log.error("广播房间 {} 聊天消息失败，发送者 {}", roomId, heroId, e);
        }
    }

    private void handleCoinTailGrab(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            // 从Redis中获取玩家会话信息
            String sessionKey = "game:session:" + clientId;
            RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
            Player sessionPlayer = redisPlayer != null ? redisPlayer.toPlayer() : null;
            
            if (sessionPlayer == null) {
                log.warn("抢金币的玩家会话不存在: {}", clientId);
                sendCoinTailGrabResult(ctx, false, 0);
                return;
            }
            
            String roomId = sessionPlayer.getRoomId();
            if (roomId == null) {
                log.warn("玩家 {} 不在任何房间中", sessionPlayer.getHeroId());
                sendCoinTailGrabResult(ctx, false, 0);
                return;
            }
            
            // 调用RoomService处理抢金币逻辑
            boolean success = roomService.grabMouseCoin(roomId, sessionPlayer.getHeroId());
            Integer mouseRemainingCoins = roomService.getRoomMouseCoinCount(roomId);
            
            // 获取玩家已经抢到的金币总数
            Integer playerTotalCoins = roomService.getPlayerTotalCoins(sessionPlayer.getHeroId());
            
            // 发送抢金币结果给请求的玩家（只要执行了grabMouseCoin，就得告知用户有没有抢成功）
            sendCoinTailGrabResult(ctx, success, playerTotalCoins);
            
            // 如果抢金币成功，广播相关消息
            if (success) {
                // 设置房间冷却状态
                roomService.setRoomCooldown(roomId);
                
                // 广播金老鼠剩余金币数量   即时没抢的人，也要知道现在金币还剩多少
                broadcastCoinOnTailGrab(roomId, mouseRemainingCoins);
                
                // 广播冷却开始消息
                broadcastCoinCool(roomId);
                
                // 启动冷却倒计时
                startCoinCoolDown(roomId);
                
                // 检查游戏是否结束   每一次抢金币成功，都需要判断游戏是否结束（金老鼠身上没金币了或者地面上没金币了）
                checkAndHandleRoomGameEnd(roomId);
            }
            
        } catch (Exception e) {
            log.error("处理抢金币消息失败", e);
            sendCoinTailGrabResult(ctx, false, 0);
        }
    }

    private void handleCoinGroundGrab(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            // 从Redis中获取玩家会话信息
            String sessionKey = "game:session:" + clientId;
            RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
            Player sessionPlayer = redisPlayer != null ? redisPlayer.toPlayer() : null;
            
            if (sessionPlayer == null) {
                log.warn("抢地面金币的玩家会话不存在: {}", clientId);
                sendCoinGroundGrabResult(ctx, message.getId(), false, 0);
                return;
            }
            
            String roomId = sessionPlayer.getRoomId();
            if (roomId == null) {
                log.warn("玩家 {} 不在任何房间中", sessionPlayer.getHeroId());
                sendCoinGroundGrabResult(ctx, message.getId(), false, 0);
                return;
            }
            
            // 调用RoomService处理抢地面金币逻辑
            boolean success = roomService.grabGroundCoin(roomId, message.getId(), sessionPlayer.getHeroId());
            
            // 获取玩家已经抢到的金币总数
            Integer playerTotalCoins = roomService.getPlayerTotalCoins(sessionPlayer.getHeroId());
            
            // 发送抢金币结果给请求的玩家
            sendCoinGroundGrabResult(ctx, message.getId(), success, playerTotalCoins);
            
            // 如果抢金币成功，广播给房间内其他玩家
            if (success) {
                broadcastCoinGroundGrabToOthers(roomId, message.getId(), sessionPlayer.getHeroId());
                
                // 检查游戏是否结束
                checkAndHandleRoomGameEnd(roomId);
            }
            
        } catch (Exception e) {
            log.error("处理抢地面金币消息失败", e);
            sendCoinGroundGrabResult(ctx, message.getId(), false, 0);
        }
    }

    private void handleMotion(String clientId, GameMessage message) {
        try {
            // 从Redis中获取玩家会话信息
            String sessionKey = "game:session:" + clientId;
            RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
            Player sessionPlayer = redisPlayer != null ? redisPlayer.toPlayer() : null;
            
            if (sessionPlayer == null) {
                log.warn("获取玩家信息失败，会话不存在: {}", clientId);
                return;
            }
            
            String roomId = sessionPlayer.getRoomId();
            if (roomId == null) {
                log.warn("玩家 {} 不在任何房间中", sessionPlayer.getHeroId());
                return;
            }
            
            // 更新玩家位置信息
            Position newPosition = new Position(message.getPosition());
            Rotation newRotation = message.getRotation();
            String newAnimation = message.getAnimation();
            
            // 更新Redis中的玩家信息
            redisPlayer.setPosition(newPosition);
            redisPlayer.setRotation(newRotation);
            redisPlayer.setAnimation(newAnimation);
            redisTemplate.opsForValue().set(sessionKey, redisPlayer);
            
            // 更新房间服务中的玩家位置
            // 可以先广播再更新redis
            if (roomService.updatePlayerPosition(sessionPlayer.getHeroId(), sessionPlayer)) {
                // 广播移动消息给同房间其他玩家
                broadcastPlayerMotion(roomId, sessionPlayer.getHeroId(), newPosition, newRotation, newAnimation);
                log.info("玩家 {} 移动，位置：({}, {}, {})", sessionPlayer.getHeroId(), newPosition.getX(), newPosition.getY(), newPosition.getZ());
            } else {
                log.warn("玩家 {} 位置更新失败", sessionPlayer.getHeroId());
            }
        } catch (Exception e) {
            log.error("处理玩家移动消息失败", e);
        }
    }

    private void handleGetHeros(ChannelHandlerContext ctx, String clientId) {
        try {
            // 从Redis中获取玩家会话信息
            String sessionKey = "game:session:" + clientId;
            RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
            Player sessionPlayer = redisPlayer != null ? redisPlayer.toPlayer() : null;
            
            if (sessionPlayer == null) {
                log.warn("获取玩家信息失败，会话不存在: {}", clientId);
                sendError(ctx, "玩家会话不存在");
                return;
            }
            
            String roomId = sessionPlayer.getRoomId();
            if (roomId == null) {
                log.warn("玩家 {} 不在任何房间中", sessionPlayer.getHeroId());
                sendError(ctx, "玩家不在任何房间中");
                return;
            }
            
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            log.info("玩家 {} 请求获取房间 {} 中的玩家列表，共 {} 个玩家", 
                sessionPlayer.getHeroId(), roomId, roomPlayers.size());
            
            // 将Player对象转换为HeroData对象，排除当前请求的玩家自己
            List<HeroData> heroDataList = roomPlayers.stream()
                    .filter(player -> !player.getHeroId().equals(sessionPlayer.getHeroId())) // 排除自己
                    .map(player -> {
                        HeroData heroData = new HeroData();
                        heroData.setHeroId(player.getHeroId());
                        heroData.setPosition(player.getPosition());
                        heroData.setRotation(player.getRotation());
                        heroData.setAnimation(player.getAnimation());
                        // 获取Hero对象
                        Hero hero = heroMapper.selectById(player.getHeroId());
                        if (hero != null && hero.getCharacterId() != null) {
                            // 根据characterId查找character并封装成GameCharacterVo
                            GameCharacter gameCharacter = gameCharacterMapper.selectById(hero.getCharacterId());
                            if (gameCharacter != null) {
                                GameCharacterVo characterVo = new GameCharacterVo();
                                characterVo.setId(gameCharacter.getId());
                                characterVo.setModelUrl(gameCharacter.getModelUrl());
                                characterVo.setPreviewUrl(gameCharacter.getPreviewUrl());
                                characterVo.setName(gameCharacter.getName());
                                characterVo.setStyleTypeId(gameCharacter.getStyleTypeId());
                                hero.setCharacter(characterVo);
                            }
                        }
                        heroData.setHero(hero);
                        return heroData;
                    })
                    .collect(java.util.stream.Collectors.toList());
            
            // 创建Heros响应对象
            Heros herosResponse = new Heros();
            herosResponse.setType("heros");
            herosResponse.setData(heroDataList);
            
            String messageJson = objectMapper.writeValueAsString(herosResponse);
            ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
            
        } catch (Exception e) {
            log.error("处理获取玩家列表消息失败", e);
            sendError(ctx, "获取玩家列表失败: " + e.getMessage());
        }
    }

    private void handleJoinGame(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        Hero hero = heroMapper.selectById(message.getHeroId());
        try {
            Player player = Player.builder()
                    .id(hero.getUserId())
                    .heroId(message.getHeroId())
                    .sessionId(clientId)
                    .channel(ctx)   // player 设置ctx 没有任何意义
                    .animation(message.getAnimation())
                    .position(new Position(message.getPosition()))
                    .rotation(message.getRotation())
                    .build();
            RoomJoinResult roomJoinResult = roomService.joinRoom(player);
            
            if (roomJoinResult.isSuccess()) {    // 业务上必须返回true，不能失败
                player.setRoomId(roomJoinResult.getRoomId());

                // 将玩家会话信息存储到Redis中（使用RedisPlayer避免序列化问题），Channel对象不能存到redis中
                String sessionKey = "game:session:" + clientId;
                RedisPlayer redisPlayer = RedisPlayer.fromPlayer(player);
                // todo game:session:{clientId} 是否需要存一个完整的player对象？
                redisTemplate.opsForValue().set(sessionKey, redisPlayer);
                
                // 发送enterSuccess消息给自己
                sendEnterSuccess(ctx);
                
                // 向同房间其他玩家发送enter消息
                broadcastPlayerEnter(roomJoinResult.getRoomId(), player);
                
                // 处理金老鼠相关消息
                handleMouseMessages(roomJoinResult.getRoomId(), player);
                
                log.info("玩家 {} 成功加入房间 {}", player.getHeroId(), roomJoinResult.getRoomId());
            } else {
                log.warn("玩家 {} 加入房间失败: {}", player.getHeroId(), roomJoinResult.getMessage());
                sendError(ctx, roomJoinResult.getMessage());
            }
        } catch (Exception e) {
            log.error("处理加入游戏消息失败", e);
            sendError(ctx, "加入游戏失败: " + e.getMessage());
        }
    }

    private void sendError(ChannelHandlerContext ctx, String errorMessage) {
        try {
            GameMessage errorMsg = new GameMessage(MessageType.error, "SERVER", null, errorMessage);
            String messageJson = objectMapper.writeValueAsString(errorMsg);
            ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
        } catch (Exception e) {
            log.error("发送错误消息失败", e);
        }
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("WebSocket异常", cause);
        ctx.close();
    }

    private void sendEnterSuccess(ChannelHandlerContext ctx) {
        try {
            String messageJson = "{\"type\":\"enterSuccess\"}";
            ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
        } catch (Exception e) {
            log.error("发送enterSuccess消息失败", e);
        }
    }

    private void broadcastPlayerEnter(String roomId, Player newPlayer) {
        try {
            // 创建HeroData对象
            HeroData heroData = new HeroData();
            heroData.setHeroId(newPlayer.getHeroId());
            heroData.setPosition(newPlayer.getPosition());
            heroData.setRotation(newPlayer.getRotation());
            heroData.setAnimation(newPlayer.getAnimation());
            
            // 获取Hero对象
            Hero hero = heroMapper.selectById(newPlayer.getHeroId());
            if (hero != null && hero.getCharacterId() != null) {
                // 根据characterId查找character并封装成GameCharacterVo
                GameCharacter gameCharacter = gameCharacterMapper.selectById(hero.getCharacterId());
                if (gameCharacter != null) {
                    GameCharacterVo characterVo = new GameCharacterVo();
                    characterVo.setId(gameCharacter.getId());
                    characterVo.setModelUrl(gameCharacter.getModelUrl());
                    characterVo.setPreviewUrl(gameCharacter.getPreviewUrl());
                    characterVo.setName(gameCharacter.getName());
                    characterVo.setStyleTypeId(gameCharacter.getStyleTypeId());
                    hero.setCharacter(characterVo);
                }
            }
            heroData.setHero(hero);
            
            // 创建enter消息
            String messageJson = objectMapper.writeValueAsString(heroData);
            String enterMessage = "{\"type\":\"enter\"," + messageJson.substring(1);
            
            // 获取房间内所有玩家,获取所有玩家的目的是获取玩家的 clientId
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            
            for (Player player : roomPlayers) {
                // 排除新加入的玩家自己
                if (newPlayer.getHeroId().equals(player.getHeroId())) {
                    continue;
                }
                
                // 通过clientId获取对应的channel
                String clientId = player.getSessionId();
                if (clientId != null) {
                    ChannelHandlerContext ctx = clients.get(clientId);
                    if (ctx != null && ctx.channel().isActive()) {
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(enterMessage));
                    }
                }
            }
            
            log.info("向房间 {} 中的其他玩家广播了新玩家 {} 的enter消息", roomId, newPlayer.getHeroId());
        } catch (Exception e) {
            log.error("广播玩家enter消息失败", e);
        }
    }
    
    private void broadcastPlayerMotion(String roomId, Integer heroId, Position position, Rotation rotation, String animation) {
        try {
            // 创建motion消息
            String motionMessage = String.format(
                "{\"type\":\"motion\",\"heroId\":%d,\"position\":{\"x\":%f,\"y\":%f,\"z\":%f},\"rotation\":{\"y\":%f},\"animation\":\"%s\"}",
                heroId, position.getX(), position.getY(), position.getZ(), rotation.getY(), animation
            );
            System.out.println(motionMessage);
            // 获取房间内所有玩家
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            
            for (Player player : roomPlayers) {
                // 排除移动的玩家自己
                if (heroId.equals(player.getHeroId())) {
                    continue;
                }
                
                // 通过clientId获取对应的channel
                String clientId = player.getSessionId();
                if (clientId != null) {
                    ChannelHandlerContext ctx = clients.get(clientId);
                    if (ctx != null && ctx.channel().isActive()) {
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(motionMessage));
                    }
                }
            }
            
            log.debug("向房间 {} 中的其他玩家广播了玩家 {} 的移动消息", roomId, heroId);
        } catch (Exception e) {
            log.error("广播玩家移动消息失败", e);
        }
    }
    
    private void broadcastEmotionMessage(String roomId, Long playerId, Integer heroId, String image) {
        try {
            // 创建emotion消息
            String emotionMessage = String.format(
                "{\"type\":\"emotion\",\"heroId\":%d,\"image\":\"%s\"}",
                heroId, image
            );
            
            // 获取房间内所有玩家
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            
            for (Player player : roomPlayers) {
                // 排除发送表情包的玩家自己
                if (heroId.equals(player.getHeroId())) {
                    continue;
                }
                
                // 通过clientId获取对应的channel
                String clientId = player.getSessionId();
                if (clientId != null) {
                    ChannelHandlerContext ctx = clients.get(clientId);
                    if (ctx != null && ctx.channel().isActive()) {
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(emotionMessage));
                    }
                }
            }
            
            log.debug("向房间 {} 中的其他玩家广播了玩家 {} 的表情包消息", roomId, playerId);
        } catch (Exception e) {
            log.error("广播表情包消息失败", e);
        }
    }
    
    private void broadcastPlayerLeave(String roomId, Integer heroId) {
        try {
            // 创建leave消息
            String leaveMessage = String.format(
                "{\"type\":\"leave\",\"heroId\":%d}",
                heroId
            );
            
            // 获取房间内所有玩家
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            
            for (Player player : roomPlayers) {
                // 通过clientId获取对应的channel
                String clientId = player.getSessionId();
                if (clientId != null) {
                    ChannelHandlerContext ctx = clients.get(clientId);
                    if (ctx != null && ctx.channel().isActive()) {
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(leaveMessage));
                    }
                }
            }
            
            log.info("向房间 {} 中的其他玩家广播了玩家 {} 的离开消息", roomId, heroId);
        } catch (Exception e) {
            log.error("广播玩家离开消息失败", e);
        }
    }

    /**
     * 处理金老鼠相关消息
     */
    private void handleMouseMessages(String roomId, Player newPlayer) {
        try {
            // 检查房间是否已有金老鼠
            Integer existingMouse = roomService.getRoomMouse(roomId);
            
            if (existingMouse != null) {
                // 房间已有金老鼠，发送coinStart消息给新玩家
                sendCoinStartMessage(newPlayer, existingMouse);
            } else {
                // 房间没有金老鼠，检查是否有掉落的金币
                List<Map<String, Object>> droppedCoins = roomService.getRoomDroppedCoins(roomId);
                if (!droppedCoins.isEmpty()) {   // 加入房间没爆金币，就不要广播
                    sendCoinBoomMessage(newPlayer, droppedCoins);
                }
            }
            // 注意：新玩家加入时不会是金老鼠，金老鼠由定时任务统一分配
        } catch (Exception e) {
            log.error("处理金老鼠消息失败", e);
        }
    }

    /**
     * 发送coinStart消息给指定玩家
     * 消息格式： {
     *     "type": "coinStart",
     *     "heroId": <金老鼠的heroId>,
     *     "coinCount": <金老鼠的金币数量>
     * }
     */
    private void sendCoinStartMessage(Player player, Integer mouseHeroId) {
        try {
            String clientId = player.getSessionId();
            if (clientId != null) {
                ChannelHandlerContext ctx = clients.get(clientId);
                if (ctx != null && ctx.channel().isActive()) {
                    String coinStartMessage = String.format(
                        "{\"type\":\"coinStart\",\"heroId\":%d,\"coinCount\":%d}",
                        mouseHeroId, roomService.getMouseCoinCount(mouseHeroId)
                    );
                    ctx.channel().writeAndFlush(new TextWebSocketFrame(coinStartMessage));
                }
            }
        } catch (Exception e) {
            log.error("发送coinStart消息失败", e);
        }
    }

    /**
     * 广播coinStart消息给房间内所有玩家，包括成为了金老鼠的玩家
     */
    public void broadcastCoinStartMessage(String roomId, Integer mouseHeroId) {
        try {
            // 获取Hero对象
            Hero hero = heroMapper.selectById(mouseHeroId);
            if (hero != null && hero.getCharacterId() != null) {
                // 根据characterId查找character并封装成GameCharacterVo
                GameCharacter gameCharacter = gameCharacterMapper.selectById(hero.getCharacterId());
                if (gameCharacter != null) {
                    GameCharacterVo characterVo = new GameCharacterVo();
                    characterVo.setId(gameCharacter.getId());
                    characterVo.setModelUrl(gameCharacter.getModelUrl());
                    characterVo.setPreviewUrl(gameCharacter.getPreviewUrl());
                    characterVo.setName(gameCharacter.getName());
                    characterVo.setStyleTypeId(gameCharacter.getStyleTypeId());
                    hero.setCharacter(characterVo);
                }
            }
            
            // 构建coinStart消息
            Map<String, Object> coinStartData = new HashMap<>();
            coinStartData.put("type", "coinStart");
            coinStartData.put("heroId", mouseHeroId);
            coinStartData.put("coinCount", roomService.getMouseCoinCount(mouseHeroId));
            coinStartData.put("hero", hero);
            
            String coinStartMessage = objectMapper.writeValueAsString(coinStartData);
            
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            for (Player player : roomPlayers) {
                String clientId = player.getSessionId();
                if (clientId != null) {
                    ChannelHandlerContext ctx = clients.get(clientId);
                    if (ctx != null && ctx.channel().isActive()) {
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(coinStartMessage));
                    }
                }
            }
            
            log.info("向房间 {} 广播了金老鼠 {} 的coinStart消息", roomId, mouseHeroId);
        } catch (Exception e) {
            log.error("广播coinStart消息失败", e);
        }
    }

    /**
     * 发送coinBoom消息给指定玩家
     */
    // 发给中途加入的新玩家，该玩家加入时，金币已经爆掉了
    private void sendCoinBoomMessage(Player player, List<Map<String, Object>> droppedCoins) {
        try {
            String clientId = player.getSessionId();
            if (clientId != null) {
                ChannelHandlerContext ctx = clients.get(clientId);
                if (ctx != null && ctx.channel().isActive()) {
                    String coinBoomMessage = createCoinBoomMessage(droppedCoins);
                    ctx.channel().writeAndFlush(new TextWebSocketFrame(coinBoomMessage));
                }
            }
        } catch (Exception e) {
            log.error("发送coinBoom消息失败", e);
        }
    }

    /**
     * 广播coinBoom消息给房间内所有玩家
     */
    private void broadcastCoinBoomMessage(String roomId) {
        try {
            List<Map<String, Object>> droppedCoins = roomService.getRoomDroppedCoins(roomId);
            if (!droppedCoins.isEmpty()) {
                String coinBoomMessage = createCoinBoomMessage(droppedCoins);
                
                List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
                for (Player player : roomPlayers) {
                    String clientId = player.getSessionId();
                    if (clientId != null) {
                        ChannelHandlerContext ctx = clients.get(clientId);
                        if (ctx != null && ctx.channel().isActive()) {
                            ctx.channel().writeAndFlush(new TextWebSocketFrame(coinBoomMessage));
                        }
                    }
                }
                
                log.info("向房间 {} 广播了coinBoom消息，掉落金币数量: {}", roomId, droppedCoins.size());
            }
        } catch (Exception e) {
            log.error("广播coinBoom消息失败", e);
        }
    }

    /**
     * 创建coinBoom消息
     */
    private String createCoinBoomMessage(List<Map<String, Object>> droppedCoins) {
        try {
            Map<String, Object> message = Map.of(
                    "type", "coinBoom",
                    "data", droppedCoins.stream()
                            .map(coin -> {
                                Object position = coin.get("position");
                                int pos = position instanceof Number ? ((Number) position).intValue() :
                                        Integer.parseInt(position.toString());
                                return Map.of(
                                        "position", pos,
                                        "id", coin.get("id").toString()
                                );
                            })
                            .collect(Collectors.toList())
            );

            return objectMapper.writeValueAsString(message);
        } catch (Exception e) {
            log.error("创建coinBoom消息失败", e);
            return "{\"type\":\"coinBoom\",\"data\":[]}";
        }
    }

    /**
     * 发送抢金币结果给指定玩家
     */
    private void sendCoinTailGrabResult(ChannelHandlerContext ctx, boolean success, Integer coinCount) {
        try {
            String resultMessage = String.format(
                "{\"type\":\"coinTailGrab\",\"success\":%s,\"coinCount\":%d}",
                success, coinCount
            );
            ctx.channel().writeAndFlush(new TextWebSocketFrame(resultMessage));
        } catch (Exception e) {
            log.error("发送抢金币结果失败", e);
        }
    }

    /**
     * 广播金老鼠剩余金币数量消息
     */
    private void broadcastCoinOnTailGrab(String roomId, Integer coinCount) {
        try {
            String message = String.format(
                "{\"type\":\"coinOnTailGrab\",\"coinCount\":%d}",
                coinCount
            );
            
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            for (Player player : roomPlayers) {
                String clientId = player.getSessionId();
                if (clientId != null) {
                    ChannelHandlerContext ctx = clients.get(clientId);
                    if (ctx != null && ctx.channel().isActive()) {
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(message));
                    }
                }
            }
            
            log.info("向房间 {} 广播了金老鼠剩余金币数量: {}", roomId, coinCount);
        } catch (Exception e) {
            log.error("广播金老鼠剩余金币数量失败", e);
        }
    }

    /**
     * 广播冷却开始消息
     */
    private void broadcastCoinCool(String roomId) {
        try {
            String message = "{\"type\":\"coinCool\"}";
            
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            for (Player player : roomPlayers) {
                String clientId = player.getSessionId();
                if (clientId != null) {
                    ChannelHandlerContext ctx = clients.get(clientId);
                    if (ctx != null && ctx.channel().isActive()) {
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(message));
                    }
                }
            }
            
            log.info("向房间 {} 广播了冷却开始消息", roomId);
        } catch (Exception e) {
            log.error("广播冷却开始消息失败", e);
        }
    }

    /**
     * 启动冷却倒计时
     */
    private void startCoinCoolDown(String roomId) {
        try {
            // 使用异步任务实现0.5秒倒计时
            new Thread(() -> {
                try {
                    Thread.sleep(500); // 0.5秒
                    broadcastCoinCoolEnd(roomId);
                } catch (InterruptedException e) {
                    log.error("冷却倒计时被中断", e);
                }
            }).start();
        } catch (Exception e) {
            log.error("启动冷却倒计时失败", e);
        }
    }

    /**
     * 广播冷却结束消息
     */
    private void broadcastCoinCoolEnd(String roomId) {
        try {
            String message = "{\"type\":\"coinCoolEnd\"}";
            
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            for (Player player : roomPlayers) {
                String clientId = player.getSessionId();
                if (clientId != null) {
                    ChannelHandlerContext ctx = clients.get(clientId);
                    if (ctx != null && ctx.channel().isActive()) {
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(message));
                    }
                }
            }
            
            log.info("向房间 {} 广播了冷却结束消息", roomId);
        } catch (Exception e) {
            log.error("广播冷却结束消息失败", e);
        }
    }

    /**
     * 发送抢地面金币结果给指定玩家
     */
    private void sendCoinGroundGrabResult(ChannelHandlerContext ctx, String coinId, boolean success, Integer coinCount) {
        try {
            String resultMessage = String.format(
                "{\"type\":\"coinGrabSelf\",\"id\":\"%s\",\"success\":%s,\"coinCount\":%d}",
                coinId, success, coinCount
            );
            ctx.channel().writeAndFlush(new TextWebSocketFrame(resultMessage));
        } catch (Exception e) {
            log.error("发送抢地面金币结果失败", e);
        }
    }

    /**
     * 广播地面金币被抢消息给房间内其他玩家
     */
    private void broadcastCoinGroundGrabToOthers(String roomId, String coinId, Integer heroId) {
        try {
            String message = String.format(
                "{\"type\":\"coinGroundGrab\",\"id\":\"%s\",\"heroId\":%d}",
                coinId, heroId
            );
            
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            for (Player player : roomPlayers) {
                // 排除抢金币的玩家自己
                if (heroId.equals(player.getHeroId())) {
                    continue;
                }
                
                String clientId = player.getSessionId();
                if (clientId != null) {
                    ChannelHandlerContext ctx = clients.get(clientId);
                    if (ctx != null && ctx.channel().isActive()) {
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(message));
                    }
                }
            }
            
            log.info("向房间 {} 广播了地面金币 {} 被玩家 {} 抢走", roomId, coinId, heroId);
        } catch (Exception e) {
            log.error("广播地面金币被抢消息失败", e);
        }
    }

    /**
     * 向所有在线玩家发送游戏结束消息
     */
    public void broadcastGameEndToAllPlayers(Map<Integer, Integer> playerCoins) {
        try {
            log.info("开始向所有在线玩家发送游戏结束消息");
            
            int sentCount = 0;
            for (Map.Entry<String, ChannelHandlerContext> entry : clients.entrySet()) {
                String clientId = entry.getKey();
                ChannelHandlerContext ctx = entry.getValue();
                
                try {
                    // 获取玩家会话信息
                    String sessionKey = "game:session:" + clientId;
                    RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
                    if (redisPlayer != null) {
                        Integer heroId = redisPlayer.getHeroId();
                        Integer coinCount = playerCoins.getOrDefault(heroId, 0);
                        
                        // 构建游戏结束消息
                        Map<String, Object> message = new HashMap<>();
                        message.put("type", "coinEnd");
                        message.put("coinCount", coinCount);
                        
                        String messageJson = objectMapper.writeValueAsString(message);
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
                        sentCount++;
                        
                        log.debug("向玩家 {} (heroId: {}) 发送游戏结束消息，金币数量: {}", clientId, heroId, coinCount);
                    }
                } catch (Exception e) {
                    log.warn("向客户端 {} 发送游戏结束消息失败: {}", clientId, e.getMessage());
                }
            }
            
            log.info("游戏结束消息发送完成，共发送给 {} 个玩家", sentCount);
        } catch (Exception e) {
            log.error("广播游戏结束消息失败", e);
        }
    }

    /**
     * 向房间内所有玩家发送游戏结束消息
     */
    public void broadcastGameEndToRoom(String roomId, Map<Integer, Integer> playerCoins) {
        try {
            log.info("开始向房间 {} 内所有玩家发送游戏结束消息", roomId);
            
            int sentCount = 0;
            for (Map.Entry<String, ChannelHandlerContext> entry : clients.entrySet()) {
                String clientId = entry.getKey();
                ChannelHandlerContext ctx = entry.getValue();
                
                try {
                    // 获取玩家会话信息
                    String sessionKey = "game:session:" + clientId;
                    RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
                    if (redisPlayer != null && roomId.equals(redisPlayer.getRoomId())) {
                        Integer heroId = redisPlayer.getHeroId();
                        Integer coinCount = playerCoins.getOrDefault(heroId, 0);
                        
                        // 构建游戏结束消息
                        Map<String, Object> message = new HashMap<>();
                        message.put("type", "coinEnd");
                        message.put("coinCount", coinCount);
                        
                        String messageJson = objectMapper.writeValueAsString(message);
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
                        sentCount++;
                        
                        log.debug("向房间 {} 玩家 {} (heroId: {}) 发送游戏结束消息，金币数量: {}", roomId, clientId, heroId, coinCount);
                    }
                } catch (Exception e) {
                    log.warn("向房间 {} 客户端 {} 发送游戏结束消息失败: {}", roomId, clientId, e.getMessage());
                }
            }
            
            log.info("房间 {} 游戏结束消息发送完成，共发送给 {} 个玩家", roomId, sentCount);
        } catch (Exception e) {
            log.error("向房间 {} 广播游戏结束消息失败", roomId, e);
        }
    }

    /**
     * 检查并处理房间游戏结束
     */
    private void checkAndHandleRoomGameEnd(String roomId) {
        try {
            // 检查游戏是否结束
            if (roomService.isRoomGameEnded(roomId)) {
                log.info("房间 {} 游戏结束，所有金币都被抢完", roomId);
                
//                // 处理房间内金老鼠的剩余金币
//                handleRoomMouseRemainingCoins(roomId);
                
                // 获取房间内所有玩家的金币统计
                Map<Integer, Integer> playerCoins = roomService.getRoomPlayersCoinStats(roomId);
                
                // 持久化金币到用户表
                if (!playerCoins.isEmpty()) {
                    roomService.persistAllPlayerCoins();
                }
                
                // 向房间内所有玩家发送游戏结束消息
                broadcastGameEndToRoom(roomId, playerCoins);
                
                // 清除房间的金币相关数据
                clearRoomCoinData(roomId);
            }
        } catch (Exception e) {
            log.error("检查房间 {} 游戏结束失败", roomId, e);
        }
    }

    /**
     * 处理房间内金老鼠的剩余金币
     */
    private void handleRoomMouseRemainingCoins(String roomId) {
        try {
            // 获取房间的金老鼠
            Integer mouseHeroId = roomService.getRoomMouse(roomId);
            if (mouseHeroId != null) {
                // 检查金老鼠是否还有剩余金币
                String mouseCoinKey = "game:mouse:" + mouseHeroId + ":coins";
                Object coinCountObj = redisTemplate.opsForValue().get(mouseCoinKey);
                
                if (coinCountObj != null) {
                    Integer remainingCoins = (Integer) coinCountObj;
                    if (remainingCoins > 0) {
                        log.info("房间 {} 金老鼠 {} 还有 {} 个剩余金币，归其所有", roomId, mouseHeroId, remainingCoins);
                        
                        // 将剩余金币添加到金老鼠的金币抢取记录中
                        String grabKey = "game:player:" + mouseHeroId + ":mouse_coins";
                        redisTemplate.opsForValue().increment(grabKey, remainingCoins);
                        
                        log.info("房间 {} 金老鼠 {} 获得剩余金币 {} 个", roomId, mouseHeroId, remainingCoins);
                    }
                }
            }
        } catch (Exception e) {
            log.error("处理房间 {} 金老鼠剩余金币失败", roomId, e);
        }
    }

    /**
     * 清除房间的金币相关数据
     */
    private void clearRoomCoinData(String roomId) {
        try {
            log.info("开始清除房间 {} 的金币相关数据", roomId);
            
            // 清除房间金币集合
            String roomCoinsKey = "room:coins:" + roomId;
            redisTemplate.delete(roomCoinsKey);
            
            // 清除房间金币位置映射
            String roomCoinPositionKey = "room:coin_position:" + roomId;
            redisTemplate.delete(roomCoinPositionKey);
            
            // 清除房间金老鼠标记
            String roomMouseKey = "game:room:" + roomId + ":mouse";
            redisTemplate.delete(roomMouseKey);
            
            // 清除房间冷却状态
            String roomCooldownKey = "game:room:" + roomId + ":cooldown";
            redisTemplate.delete(roomCooldownKey);
            
            // 清除房间内所有玩家的金老鼠金币抢取记录
            List<Player> players = roomService.getPlayersInRoom(roomId);
            for (Player player : players) {
                String mouseCoinGrabKey = "game:player:" + player.getHeroId() + ":mouse_coins";
                redisTemplate.delete(mouseCoinGrabKey);
            }
            
            log.info("房间 {} 金币相关数据清除完成", roomId);
        } catch (Exception e) {
            log.error("清除房间 {} 金币相关数据失败", roomId, e);
        }
    }

    /**
     * 处理玩家从广场掉落的消息
     */
    private void handleDropGround(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            // 从Redis中获取玩家会话信息
            String sessionKey = "game:session:" + clientId;
            RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
            Player sessionPlayer = redisPlayer != null ? redisPlayer.toPlayer() : null;
            
            if (sessionPlayer == null) {
                log.warn("掉落的玩家会话不存在: {}", clientId);
                return;
            }
            
            String roomId = sessionPlayer.getRoomId();
            if (roomId == null) {
                log.warn("玩家 {} 不在任何房间中", sessionPlayer.getHeroId());
                return;
            }
            
            Integer heroId = sessionPlayer.getHeroId();
            Position newPosition = message.getPosition();
            
            if (newPosition == null) {
                log.warn("玩家 {} 掉落位置为空", heroId);
                return;
            }
            
            log.info("玩家 {} 从广场掉落，新位置: ({}, {}, {})", heroId, newPosition.getX(), newPosition.getY(), newPosition.getZ());
            
            // 更新玩家位置
            sessionPlayer.setPosition(newPosition);
            roomService.updatePlayerPosition(heroId, sessionPlayer);
            
            // 广播玩家掉落消息给同房间其他玩家
            broadcastAnnounceDrop(roomId, heroId, newPosition);
            
            // 检查是否为金老鼠，如果是则处理金币掉落
            checkAndHandleMouseCoinDrop(roomId, heroId);
            
        } catch (Exception e) {
            log.error("处理玩家掉落消息失败", e);
        }
    }

    /**
     * 广播玩家掉落消息给同房间其他玩家
     */
    private void broadcastAnnounceDrop(String roomId, Integer heroId, Position position) {
        try {
            log.info("向房间 {} 广播玩家 {} 掉落消息", roomId, heroId);

            // 直接手工构造 JSON 字符串
            String messageJson = String.format(
                "{\"type\":\"announceDrop\",\"heroId\":%d,\"position\":{\"x\":%s,\"y\":%s,\"z\":%s}}",
                heroId,
                position.getX(),
                position.getY(),
                position.getZ()
            );

            int sentCount = 0;

            // 获取房间内所有玩家
            List<Player> players = roomService.getPlayersInRoom(roomId);
            for (Player player : players) {
                // 排除掉落的玩家自己
                if (player.getHeroId().equals(heroId)) {
                    continue;
                }
                try {
                    String clientId = player.getSessionId();
                    if (clientId != null) {
                        ChannelHandlerContext ctx = clients.get(clientId);
                        if (ctx != null && ctx.channel().isActive()) {
                            ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
                            sentCount++;
                            log.debug("向房间 {} 玩家 {} 发送掉落广播消息", roomId, player.getHeroId());
                        }
                    }
                } catch (Exception e) {
                    log.warn("向房间 {} 玩家 {} 发送掉落广播消息失败: {}", roomId, player.getHeroId(), e.getMessage());
                }
            }

            log.info("房间 {} 掉落广播消息发送完成，共发送给 {} 个玩家", roomId, sentCount);
        } catch (Exception e) {
            log.error("向房间 {} 广播掉落消息失败", roomId, e);
        }
    }

    /**
     * 检查并处理金老鼠金币掉落
     */
    private void checkAndHandleMouseCoinDrop(String roomId, Integer heroId) {
        try {
            // 检查该玩家是否为金老鼠
            Integer roomMouse = roomService.getRoomMouse(roomId);
            if (roomMouse != null && roomMouse.equals(heroId)) {
                log.info("玩家 {} 是金老鼠，处理金币掉落", heroId);

                // 获取金老鼠的金币数量
                Integer coinCount = roomService.getMouseCoinCount(heroId);
                // 掉落金币到广场
                roomService.dropMouseCoins(roomId, heroId);
                // 如果掉落了金币，广播coinBoom消息给房间内其他玩家
                broadcastCoinBoomMessage(roomId);
                if (coinCount > 0) {
                    // 掉落金币到广场
                    //roomService.dropMouseCoins(roomId, heroId);

                    // 广播金币掉落消息给房间内所有玩家
                    broadcastCoinDropMessage(roomId, coinCount);
                    
                    log.info("金老鼠 {} 掉落了 {} 个金币到广场", heroId, coinCount);
                }
            }
        } catch (Exception e) {
            log.error("检查并处理金老鼠金币掉落失败", e);
        }
    }

    /**
     * 广播金币掉落消息
     */
    private void broadcastCoinDropMessage(String roomId, Integer coinCount) {
        try {
            log.info("向房间 {} 广播金币掉落消息，金币数量: {}", roomId, coinCount);

            // 直接构造json字符串
            String messageJson = String.format("{\"type\":\"dropGround\",\"coinCount\":%d}", coinCount);
            int sentCount = 0;

            // 获取房间内所有玩家
            List<Player> players = roomService.getPlayersInRoom(roomId);
            for (Player player : players) {
                try {
                    String clientId = player.getSessionId();
                    if (clientId != null) {
                        ChannelHandlerContext ctx = clients.get(clientId);
                        if (ctx != null && ctx.channel().isActive()) {
                            ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
                            sentCount++;
                            log.debug("向房间 {} 玩家 {} 发送金币掉落消息", roomId, player.getHeroId());
                        }
                    }
                } catch (Exception e) {
                    log.warn("向房间 {} 玩家 {} 发送金币掉落消息失败: {}", roomId, player.getHeroId(), e.getMessage());
                }
            }

            log.info("房间 {} 金币掉落消息发送完成，共发送给 {} 个玩家", roomId, sentCount);
        } catch (Exception e) {
            log.error("向房间 {} 广播金币掉落消息失败", roomId, e);
        }
    }

    private void handleGameMatch(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            String sessionKey = "game:session:" + clientId;
            RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
            Player sessionPlayer = redisPlayer != null ? redisPlayer.toPlayer() : null;
            
            if (sessionPlayer == null) {
                log.warn("匹配游戏的玩家会话不存在: {}", clientId);
                sendError(ctx, "玩家会话不存在");
                return;
            }
            
            Integer heroId = sessionPlayer.getHeroId();
            Integer gameId = message.getGameId();
            
            if (gameId == null || gameId < 1 || gameId > 5) {
                log.warn("玩家 {} 提供的游戏ID无效: {}", heroId, gameId);
                sendError(ctx, "游戏ID无效，必须在1-5之间");
                return;
            }
            
            boolean success = gameRoomService.joinGameMatch(heroId, gameId);
            if (success) {
                sendGameMatchResult(ctx, true, gameId);
                log.info("玩家 {} 成功加入游戏 {} 匹配", heroId, gameId);
            } else {
                sendGameMatchResult(ctx, false, gameId);
                log.warn("玩家 {} 加入游戏 {} 匹配失败", heroId, gameId);
            }
            
        } catch (Exception e) {
            log.error("处理游戏匹配消息失败", e);
            sendError(ctx, "游戏匹配失败: " + e.getMessage());
        }
    }

    private void handleCancelMatch(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            String sessionKey = "game:session:" + clientId;
            RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
            Player sessionPlayer = redisPlayer != null ? redisPlayer.toPlayer() : null;
            
            if (sessionPlayer == null) {
                log.warn("取消匹配的玩家会话不存在: {}", clientId);
                sendError(ctx, "玩家会话不存在");
                return;
            }
            
            Integer heroId = sessionPlayer.getHeroId();
            Integer gameId = message.getGameId();
            
            if (gameId == null || gameId < 1 || gameId > 5) {
                log.warn("玩家 {} 提供的游戏ID无效: {}", heroId, gameId);
                sendError(ctx, "游戏ID无效，必须在1-5之间");
                return;
            }
            
            boolean success = gameRoomService.cancelGameMatch(heroId, gameId);
            if (success) {
                sendCancelMatchResult(ctx, true, gameId);
                log.info("玩家 {} 成功取消游戏 {} 匹配", heroId, gameId);
            } else {
                sendCancelMatchResult(ctx, false, gameId);
                log.warn("玩家 {} 取消游戏 {} 匹配失败", heroId, gameId);
            }
            
        } catch (Exception e) {
            log.error("处理取消匹配消息失败", e);
            sendError(ctx, "取消匹配失败: " + e.getMessage());
        }
    }

    private void handleGameEnd(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            String sessionKey = "game:session:" + clientId;
            RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
            Player sessionPlayer = redisPlayer != null ? redisPlayer.toPlayer() : null;
            
            if (sessionPlayer == null) {
                log.warn("游戏结束的玩家会话不存在: {}", clientId);
                sendError(ctx, "玩家会话不存在");
                return;
            }
            
            Integer heroId = sessionPlayer.getHeroId();
            Position returnPosition = message.getPosition();
            
            if (returnPosition == null) {
                log.warn("玩家 {} 返回位置为空", heroId);
                sendError(ctx, "返回位置不能为空");
                return;
            }
            
            // 获取玩家当前所在的广场房间
            String currentRoomId = sessionPlayer.getRoomId();
            if (currentRoomId == null) {
                log.warn("玩家 {} 不在任何广场房间中", heroId);
                sendError(ctx, "玩家不在广场房间中");
                return;
            }
            
            // 更新玩家位置信息
            sessionPlayer.setPosition(returnPosition);
            roomService.updatePlayerPosition(heroId, sessionPlayer);
            
            // 更新Redis中的玩家位置信息
            redisPlayer.setPosition(returnPosition);
            redisTemplate.opsForValue().set(sessionKey, redisPlayer);
            
            // 广播玩家返回广场消息给同广场的其他玩家
            broadcastPlayerReturnToGround(currentRoomId, heroId, returnPosition);
            
            log.info("玩家 {} 从小游戏返回广场，位置: ({}, {}, {})", heroId, 
                returnPosition.getX(), returnPosition.getY(), returnPosition.getZ());
            
        } catch (Exception e) {
            log.error("处理游戏结束消息失败", e);
            sendError(ctx, "处理游戏结束失败: " + e.getMessage());
        }
    }

    public void broadcastGameStart(Integer gameId, String roomId, List<Integer> playerIds) {
        try {
            log.info("广播游戏 {} 房间 {} 开始消息，玩家列表: {}", gameId, roomId, playerIds);
            
            String messageJson = String.format(
                "{\"type\":\"gameStart\",\"gameId\":%d,\"roomId\":\"%s\"}",
                gameId, roomId
            );
            
            int sentCount = 0;
            for (Integer heroId : playerIds) {
                for (Map.Entry<String, ChannelHandlerContext> entry : clients.entrySet()) {
                    String clientId = entry.getKey();
                    ChannelHandlerContext ctx = entry.getValue();
                    
                    try {
                        String sessionKey = "game:session:" + clientId;
                        RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
                        if (redisPlayer != null && heroId.equals(redisPlayer.getHeroId())) {
                            ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
                            sentCount++;
                            log.debug("向玩家 {} (heroId: {}) 发送游戏开始消息", clientId, heroId);
                        }
                    } catch (Exception e) {
                        log.warn("向客户端 {} 发送游戏开始消息失败: {}", clientId, e.getMessage());
                    }
                }
            }
            
            log.info("游戏开始消息发送完成，共发送给 {} 个玩家", sentCount);
        } catch (Exception e) {
            log.error("广播游戏开始消息失败", e);
        }
    }

    private void sendGameMatchResult(ChannelHandlerContext ctx, boolean success, Integer gameId) {
        try {
            String resultMessage = String.format(
                "{\"type\":\"gameMatch\",\"success\":%s,\"gameId\":%d}",
                success, gameId
            );
            ctx.channel().writeAndFlush(new TextWebSocketFrame(resultMessage));
        } catch (Exception e) {
            log.error("发送游戏匹配结果失败", e);
        }
    }

    private void sendCancelMatchResult(ChannelHandlerContext ctx, boolean success, Integer gameId) {
        try {
            String resultMessage = String.format(
                "{\"type\":\"cancelMatch\",\"success\":%s,\"gameId\":%d}",
                success, gameId
            );
            ctx.channel().writeAndFlush(new TextWebSocketFrame(resultMessage));
        } catch (Exception e) {
            log.error("发送取消匹配结果失败", e);
        }
    }

    private void broadcastPlayerReturnToGround(String roomId, Integer heroId, Position position) {
        try {
            log.info("向广场房间 {} 广播玩家 {} 返回广场消息，位置: ({}, {}, {})", 
                roomId, heroId, position.getX(), position.getY(), position.getZ());

            // 创建returnGround消息
            String returnGroundMessage = String.format(
                "{\"type\":\"returnGround\",\"heroId\":%d,\"position\":{\"x\":%f,\"y\":%f,\"z\":%f}}",
                heroId, position.getX(), position.getY(), position.getZ()
            );
            
            // 获取广场房间内所有玩家
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            int sentCount = 0;
            
            for (Player player : roomPlayers) {
                // 排除返回广场的玩家自己
                if (heroId.equals(player.getHeroId())) {
                    continue;
                }
                
                try {
                    String clientId = player.getSessionId();
                    if (clientId != null) {
                        ChannelHandlerContext ctx = clients.get(clientId);
                        if (ctx != null && ctx.channel().isActive()) {
                            ctx.channel().writeAndFlush(new TextWebSocketFrame(returnGroundMessage));
                            sentCount++;
                            log.debug("向广场房间 {} 玩家 {} 发送玩家 {} 返回广场消息", roomId, player.getHeroId(), heroId);
                        }
                    }
                } catch (Exception e) {
                    log.warn("向广场房间 {} 玩家 {} 发送返回广场消息失败: {}", roomId, player.getHeroId(), e.getMessage());
                }
            }
            
            log.info("广场房间 {} 玩家返回广场消息发送完成，共发送给 {} 个玩家", roomId, sentCount);
        } catch (Exception e) {
            log.error("广播玩家返回广场消息失败", e);
        }
    }

    public void broadcastCoinBoom(String roomId, Integer mouseHeroId, List<Map<String, Object>> coinBoomData) {
        try {
            log.info("向广场房间 {} 广播金老鼠玩家 {} 的金币爆炸消息，金币数量: {}", 
                roomId, mouseHeroId, coinBoomData.size());

            // 创建coinBoom消息
            String coinBoomMessage = String.format(
                "{\"type\":\"coinBoom\",\"data\":%s}",
                new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(coinBoomData)
            );

            // 获取广场房间内所有玩家
            List<Player> roomPlayers = roomService.getPlayersInRoom(roomId);
            int sentCount = 0;

            for (Player player : roomPlayers) {
                // 排除金老鼠玩家自己
                if (mouseHeroId.equals(player.getHeroId())) {
                    continue;
                }

                try {
                    String clientId = player.getSessionId();
                    if (clientId != null) {
                        ChannelHandlerContext ctx = clients.get(clientId);
                        if (ctx != null && ctx.channel().isActive()) {
                            ctx.channel().writeAndFlush(new TextWebSocketFrame(coinBoomMessage));
                            sentCount++;
                            log.debug("向广场房间 {} 玩家 {} 发送金币爆炸消息", roomId, player.getHeroId());
                        }
                    }
                } catch (Exception e) {
                    log.warn("向广场房间 {} 玩家 {} 发送金币爆炸消息失败: {}", roomId, player.getHeroId(), e.getMessage());
                }
            }

            log.info("广场房间 {} 金币爆炸消息发送完成，共发送给 {} 个玩家", roomId, sentCount);
        } catch (Exception e) {
            log.error("广播金币爆炸消息失败", e);
        }
    }
}
